#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Outlook邮件自动获取脚本
使用Microsoft Graph API和OAuth 2.0认证
"""

import requests
import json
import webbrowser
import urllib.parse
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading
import time
from datetime import datetime
import os
import configparser

class OutlookMailClient:
    """
    Outlook邮件客户端类
    用于OAuth 2.0认证和邮件获取
    """
    
    def __init__(self, client_id, redirect_uri, config_file="config.ini"):
        """
        初始化邮件客户端
        
        Args:
            client_id (str): 应用程序客户端ID
            redirect_uri (str): 重定向URI
            config_file (str): 配置文件路径
        """
        self.client_id = client_id
        self.redirect_uri = redirect_uri
        self.config_file = config_file
        self.access_token = None
        self.refresh_token = None
        self.auth_code = None
        
        # Microsoft Graph API端点
        self.auth_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
        self.token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
        self.graph_url = "https://graph.microsoft.com/v1.0"
        
        # 权限范围
        self.scopes = "offline_access user.read mail.read mail.send"
        
        # 加载已保存的令牌
        self.load_tokens()
    
    def get_authorization_url(self):
        """
        生成OAuth 2.0授权URL
        
        Returns:
            str: 授权URL
        """
        params = {
            'client_id': self.client_id,
            'response_type': 'code',
            'redirect_uri': self.redirect_uri,
            'response_mode': 'query',
            'scope': self.scopes,
            'state': '12345'  # 防止CSRF攻击的状态参数
        }
        
        auth_url = f"{self.auth_url}?{urllib.parse.urlencode(params)}"
        return auth_url
    
    def start_auth_server(self):
        """
        启动本地HTTP服务器接收授权回调
        """
        outer_self = self
        
        class AuthHandler(BaseHTTPRequestHandler):
            def do_GET(self):
                # 解析授权码
                if '?code=' in self.path:
                    query = urllib.parse.urlparse(self.path).query
                    params = urllib.parse.parse_qs(query)
                    if 'code' in params:
                        outer_self.auth_code = params['code'][0]
                        print(f"收到授权码: {outer_self.auth_code[:20]}...")
                        
                        # 返回成功页面
                        self.send_response(200)
                        self.send_header('Content-type', 'text/html; charset=utf-8')
                        self.end_headers()
                        success_html = '''
                        <html>
                        <head><title>授权成功</title></head>
                        <body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
                            <h1 style="color: green;">✅ 授权成功！</h1>
                            <p>您可以关闭此窗口，返回到应用程序。</p>
                            <script>setTimeout(function(){window.close();}, 3000);</script>
                        </body>
                        </html>
                        '''
                        self.wfile.write(success_html.encode('utf-8'))
                        return
                
                # 返回错误页面
                self.send_response(400)
                self.send_header('Content-type', 'text/html; charset=utf-8')
                self.end_headers()
                error_html = '''
                <html>
                <head><title>授权失败</title></head>
                <body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
                    <h1 style="color: red;">❌ 授权失败</h1>
                    <p>请检查授权链接或重试。</p>
                </body>
                </html>
                '''
                self.wfile.write(error_html.encode('utf-8'))
            
            def log_message(self, format, *args):
                # 禁用日志输出
                pass
        
        # 尝试不同端口启动服务器
        ports_to_try = [8000, 8001, 8002, 8080, 3000]
        server = None
        
        for port in ports_to_try:
            try:
                server = HTTPServer(('localhost', port), AuthHandler)
                print(f"HTTP服务器已启动在端口 {port}")
                # 更新重定向URI
                self.redirect_uri = f"http://localhost:{port}"
                break
            except OSError as e:
                if port == ports_to_try[-1]:  # 最后一个端口也失败
                    print(f"无法启动HTTP服务器，所有端口都被占用: {e}")
                    return None
                continue
        
        if server:
            # 在新线程中运行服务器
            server_thread = threading.Thread(target=server.serve_forever)
            server_thread.daemon = True
            server_thread.start()
        
        return server
    
    def authenticate(self, force_refresh=False):
        """
        执行OAuth 2.0认证流程
        
        Args:
            force_refresh (bool): 是否强制重新认证以获取新的refresh_token
        
        Returns:
            bool: 认证是否成功
        """
        # 如果强制刷新或没有有效令牌，重新认证
        if force_refresh or not self.access_token or not self.verify_token():
            if force_refresh:
                print("强制重新认证以获取新的refresh_token...")
            else:
                print("开始OAuth 2.0认证流程...")
            
            # 启动本地服务器
            server = self.start_auth_server()
            if not server:
                print("无法启动HTTP服务器，请检查端口占用情况")
                return False
            
            # 获取授权URL并打开浏览器
            auth_url = self.get_authorization_url()
            print(f"请在浏览器中完成授权: {auth_url}")
            webbrowser.open(auth_url)
            
            # 等待授权码
            print("等待授权回调...")
            timeout = 120  # 120秒超时
            start_time = time.time()
            
            while not self.auth_code and (time.time() - start_time) < timeout:
                time.sleep(1)
            
            server.shutdown()
            
            if not self.auth_code:
                print("授权超时，请重试")
                return False
            
            print("收到授权码，正在获取访问令牌和refresh_token...")
            return self.get_access_token()
        else:
            print("使用已保存的有效令牌")
            # 即使token有效，也刷新一次以获取最新的refresh_token
            if self.refresh_token:
                print("刷新token以获取最新的refresh_token...")
                self.refresh_access_token()
            return True
    
    def get_access_token(self):
        """
        使用授权码获取访问令牌和refresh_token
        
        Returns:
            bool: 是否成功获取令牌
        """
        data = {
            'client_id': self.client_id,
            'scope': self.scopes,
            'code': self.auth_code,
            'redirect_uri': self.redirect_uri,
            'grant_type': 'authorization_code'
        }
        
        headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
        
        try:
            # 保存旧的refresh_token用于对比
            old_refresh_token = self.refresh_token
            
            response = requests.post(self.token_url, data=data, headers=headers)
            response.raise_for_status()
            
            token_data = response.json()
            self.access_token = token_data.get('access_token')
            new_refresh_token = token_data.get('refresh_token')
            
            if new_refresh_token:
                print("\n=== Refresh Token 更新 ===")
                if old_refresh_token:
                    print(f"旧的 refresh_token: {old_refresh_token[:50]}...{old_refresh_token[-20:]}")
                else:
                    print("旧的 refresh_token: (无)")
                print(f"新的 refresh_token: {new_refresh_token[:50]}...{new_refresh_token[-20:]}")
                print("=" * 50)
                
                self.refresh_token = new_refresh_token
                print("成功获取访问令牌和refresh_token")
            elif self.access_token:
                print("成功获取访问令牌（未返回refresh_token）")
            
            if self.access_token:
                # 保存令牌到配置文件（包括refresh_token）
                self.save_tokens()
                return True
            else:
                print("获取访问令牌失败")
                return False
                
        except requests.exceptions.RequestException as e:
            print(f"获取访问令牌时发生错误: {e}")
            if hasattr(e, 'response') and e.response is not None:
                try:
                    error_detail = e.response.json()
                    print(f"错误详情: {error_detail}")
                except:
                    print(f"响应内容: {e.response.text}")
            return False
    
    def get_user_info(self):
        """
        获取当前用户信息
        
        Returns:
            dict: 用户信息字典
        """
        if not self.access_token:
            print("请先进行认证")
            return None
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        try:
            response = requests.get(f"{self.graph_url}/me", headers=headers)
            response.raise_for_status()
            return response.json()
            
        except requests.exceptions.RequestException as e:
            print(f"获取用户信息时发生错误: {e}")
            return None
    
    def get_inbox_messages(self, count=10):
        """
        获取收件箱邮件
        
        Args:
            count (int): 获取邮件数量，默认10封
            
        Returns:
            list: 邮件列表
        """
        if not self.access_token:
            print("请先进行认证")
            return []
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        # 构建查询参数
        params = {
            '$top': count,
            '$select': 'subject,from,receivedDateTime,bodyPreview,isRead',
            '$orderby': 'receivedDateTime desc'
        }
        
        try:
            response = requests.get(
                f"{self.graph_url}/me/mailFolders/inbox/messages",
                headers=headers,
                params=params
            )
            response.raise_for_status()
            
            data = response.json()
            return data.get('value', [])
            
        except requests.exceptions.RequestException as e:
            print(f"获取邮件时发生错误: {e}")
            return []
    
    def send_email(self, to_email, subject, body, body_type='Text', from_email=None):
        """
        发送邮件
        
        Args:
            to_email (str): 收件人邮箱
            subject (str): 邮件主题
            body (str): 邮件内容
            body_type (str): 内容类型，'Text' 或 'HTML'
            from_email (str): 代理发件人邮箱，如果提供则使用该邮箱发送（需要已授权）
            
        Returns:
            bool: 是否发送成功
        """
        if not self.access_token:
            print("请先进行认证")
            return False
        
        # 发送邮件前，刷新token以获取最新的refresh_token
        if self.refresh_token:
            print("发送邮件前刷新token以获取最新的refresh_token...")
            self.refresh_access_token()
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        # 构建邮件数据
        message_data = {
            'message': {
                'subject': subject,
                'body': {
                    'contentType': body_type,
                    'content': body
                },
                'toRecipients': [
                    {
                        'emailAddress': {
                            'address': to_email
                        }
                    }
                ]
            }
        }
        
        # 如果指定了代理邮箱，添加from字段
        # 注意：使用委托权限时，应使用 /me/sendMail 端点，并在消息中指定 from 字段
        # 这要求当前认证用户有"Send on behalf"权限
        if from_email:
            message_data['message']['from'] = {
                'emailAddress': {
                    'address': from_email
                }
            }
            print(f"使用代理邮箱发送: {from_email}")
            print("注意：需要当前用户有该邮箱的'Send on behalf'权限")
        
        # 使用当前用户的API端点（委托权限）
        # 如果指定了from_email，消息将从该邮箱发送（如果权限允许）
        api_endpoint = f"{self.graph_url}/me/sendMail"
        
        try:
            response = requests.post(
                api_endpoint,
                headers=headers,
                json=message_data
            )
            response.raise_for_status()
            sender_info = f" (代理邮箱: {from_email})" if from_email else ""
            print(f"邮件已成功发送到: {to_email}{sender_info}")
            return True
            
        except requests.exceptions.RequestException as e:
            print(f"发送邮件时发生错误: {e}")
            if hasattr(e.response, 'text'):
                print(f"错误详情: {e.response.text}")
            return False
    
    def display_messages(self, messages):
        """
        格式化显示邮件列表
        
        Args:
            messages (list): 邮件列表
        """
        if not messages:
            print("没有找到邮件")
            return
        
        print(f"\n找到 {len(messages)} 封邮件:")
        print("-" * 80)
        
        for i, message in enumerate(messages, 1):
            # 格式化时间
            received_time = datetime.fromisoformat(
                message['receivedDateTime'].replace('Z', '+00:00')
            ).strftime('%Y-%m-%d %H:%M:%S')
            
            # 获取发件人
            sender = message.get('from', {}).get('emailAddress', {}).get('address', '未知发件人')
            
            # 读取状态
            read_status = "已读" if message.get('isRead') else "未读"
            
            print(f"{i}. 主题: {message.get('subject', '无主题')}")
            print(f"   发件人: {sender}")
            print(f"   时间: {received_time}")
            print(f"   状态: {read_status}")
            print(f"   预览: {message.get('bodyPreview', '')[:100]}...")
            print("-" * 80)
    
    def load_tokens(self):
        """
        从配置文件加载已保存的令牌
        """
        if not os.path.exists(self.config_file):
            print("配置文件不存在，将创建新的配置文件")
            return
        
        try:
            config = configparser.ConfigParser()
            config.read(self.config_file, encoding='utf-8')
            
            if 'tokens' in config:
                self.access_token = config['tokens'].get('access_token')
                self.refresh_token = config['tokens'].get('refresh_token')
                
                if self.access_token:
                    print("从配置文件加载了已保存的令牌")
                    # 验证令牌是否仍然有效
                    if self.verify_token():
                        print("令牌验证成功，可以直接使用")
                    else:
                        print("令牌已过期，尝试刷新令牌")
                        if not self.refresh_access_token():
                            print("刷新令牌失败，需要重新认证")
                            self.access_token = None
                            self.refresh_token = None
        except Exception as e:
            print(f"加载配置文件时发生错误: {e}")
    
    def save_tokens(self):
        """
        保存令牌到配置文件
        """
        try:
            config = configparser.ConfigParser()
            
            # 如果配置文件存在，先读取现有配置
            if os.path.exists(self.config_file):
                config.read(self.config_file, encoding='utf-8')
            
            # 添加或更新tokens部分
            if 'tokens' not in config:
                config.add_section('tokens')
            
            if self.access_token:
                config['tokens']['access_token'] = self.access_token
            if self.refresh_token:
                config['tokens']['refresh_token'] = self.refresh_token
            
            # 添加保存时间戳
            config['tokens']['saved_at'] = datetime.now().isoformat()
            
            # 写入配置文件
            with open(self.config_file, 'w', encoding='utf-8') as f:
                config.write(f)
            
            print(f"令牌已保存到配置文件: {self.config_file}")
            
        except Exception as e:
            print(f"保存配置文件时发生错误: {e}")
    
    def verify_token(self):
        """
        验证访问令牌是否有效
        
        Returns:
            bool: 令牌是否有效
        """
        if not self.access_token:
            return False
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
        
        try:
            response = requests.get(f"{self.graph_url}/me", headers=headers, timeout=10)
            return response.status_code == 200
        except:
            return False
    
    def refresh_access_token(self):
        """
        使用刷新令牌获取新的访问令牌和refresh_token
        
        Returns:
            bool: 是否成功刷新令牌
        """
        if not self.refresh_token:
            print("没有刷新令牌，无法自动刷新")
            return False
        
        data = {
            'client_id': self.client_id,
            'scope': self.scopes,
            'refresh_token': self.refresh_token,
            'grant_type': 'refresh_token'
        }
        
        headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
        
        try:
            # 保存旧的refresh_token用于对比
            old_refresh_token = self.refresh_token
            
            response = requests.post(self.token_url, data=data, headers=headers)
            response.raise_for_status()
            
            token_data = response.json()
            self.access_token = token_data.get('access_token')
            
            # 如果返回了新的刷新令牌，更新它；否则保持原有refresh_token
            # 注意：Microsoft有时不会返回新的refresh_token，但我们应该保存当前的
            if 'refresh_token' in token_data and token_data['refresh_token']:
                new_refresh_token = token_data['refresh_token']
                
                print("\n=== Refresh Token 更新 ===")
                if old_refresh_token:
                    print(f"旧的 refresh_token: {old_refresh_token[:50]}...{old_refresh_token[-20:]}")
                else:
                    print("旧的 refresh_token: (无)")
                print(f"新的 refresh_token: {new_refresh_token[:50]}...{new_refresh_token[-20:]}")
                
                # 检查是否真的改变了
                if old_refresh_token and old_refresh_token != new_refresh_token:
                    print("✓ refresh_token 已更新")
                elif old_refresh_token:
                    print("⚠ refresh_token 未改变（与旧值相同）")
                else:
                    print("✓ 首次获取 refresh_token")
                print("=" * 50)
                
                self.refresh_token = new_refresh_token
                print("已获取新的refresh_token")
            else:
                print("\n=== Refresh Token 状态 ===")
                if old_refresh_token:
                    print(f"当前 refresh_token: {old_refresh_token[:50]}...{old_refresh_token[-20:]}")
                    print("⚠ API未返回新的refresh_token，保持现有值")
                else:
                    print("⚠ 没有refresh_token可用")
                print("=" * 50)
            
            if self.access_token:
                print("成功刷新访问令牌")
                # 总是保存tokens（包括refresh_token）到配置文件
                self.save_tokens()
                return True
            else:
                print("刷新令牌失败")
                return False
                
        except requests.exceptions.RequestException as e:
            print(f"刷新令牌时发生错误: {e}")
            if hasattr(e, 'response') and e.response is not None:
                try:
                    error_detail = e.response.json()
                    print(f"错误详情: {error_detail}")
                except:
                    print(f"响应内容: {e.response.text}")
            return False


def main():
    """
    主函数 - 演示邮件客户端的使用
    """
    # 配置参数
    CLIENT_ID = "acf0a6ea-e76d-4550-b031-b9bca70cf07d"
    REDIRECT_URI = "http://localhost:8000"
    
    # 创建邮件客户端
    client = OutlookMailClient(CLIENT_ID, REDIRECT_URI)
    
    print("=== Outlook邮件自动获取脚本 ===")
    
    # 执行认证（每次登录都重新获取refresh_token）
    # 如果需要强制重新认证，可以设置 force_refresh=True
    if not client.authenticate(force_refresh=False):
        print("认证失败，程序退出")
        return
    
    # 获取用户信息
    user_info = client.get_user_info()
    if user_info:
        print(f"\n欢迎, {user_info.get('displayName', '用户')}!")
        print(f"邮箱: {user_info.get('mail') or user_info.get('userPrincipalName')}")
    
    # 获取并显示收件箱邮件
    print("\n正在获取收件箱邮件...")
    messages = client.get_inbox_messages(count=5)
    client.display_messages(messages)
    
    # 演示发送邮件（可选）
    if user_info and input("\n是否要发送测试邮件？(y/n): ").lower() == 'y':
        user_email = user_info.get('mail') or user_info.get('userPrincipalName')
        if user_email:
            # 使用代理邮箱发送邮件
            proxy_email = "ttcpic@tagtimeasia.com"
            success = client.send_email(
                to_email="2504880498@qq.com",
                subject="2222222",
                body="11113333,今天周五了，周末双休。",
                from_email=proxy_email  # 使用代理邮箱发送
            )
            if success:
                print("测试邮件发送成功！")


if __name__ == "__main__":
    main()